package com.mongodb.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.constants.GeoJsonConstants;
import com.mongodb.constants.GeoJsonTypeConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.geo.GeoJson;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;

import java.util.ArrayList;
import java.util.List;

public class GeoJsonUtils {
    /**
     * 根据geometry格式的jsonObject新建一个geoJson对象
     *
     * @param geometryIn
     * @return
     */
    public static GeoJson newGeoJson(JSONObject geometryIn) {
        return new GeoJson() {
            @Override
            public String getType() {
                return GeoJsonUtils.getType(geometryIn);
            }

            @Override
            public Iterable<?> getCoordinates() {
                return GeoJsonUtils.getCoordinates(geometryIn);
            }
        };
    }

    /**
     * 获取点的坐标
     *
     * @param geoJson
     * @return
     */
    public static List<Double> getPointCoordinates(JSONObject geoJson) {
        JSONArray pointArray = geoJson.getJSONArray(GeoJsonConstants.COORDINATES);
        return pointArray.toJavaList(Double.class);
    }

    /**
     * 获取线的坐标
     *
     * @param geoJson
     * @return
     */
    public static List<List<Double>> getLineStringCoordinates(JSONObject geoJson) {
        List<List<Double>> rLineList = new ArrayList<>();
        JSONArray lineArray = geoJson.getJSONArray(GeoJsonConstants.COORDINATES);
        List<?> lineList = lineArray.toJavaList((Class<? extends Object>) List.class);
        lineList.forEach(line -> {
            JSONArray parseLineArray = JSONArray.parseArray(line.toString());
            List<Double> pointList = parseLineArray.toJavaList(Double.class);
            rLineList.add(pointList);
        });
        return rLineList;
    }

    /**
     * 获取面的坐标
     *
     * @param geoJson
     * @return
     */
    public static List<List<List<Double>>> getPolygonCoordinates(JSONObject geoJson) {
        List<List<List<Double>>> rPolygonList = new ArrayList<>();
        JSONArray polygonArray = geoJson.getJSONArray(GeoJsonConstants.COORDINATES);
        List<?> polygonList = polygonArray.toJavaList((Class<? extends Object>) List.class);
        polygonList.forEach(polygon -> {
            JSONArray parseLineArray = JSONArray.parseArray(polygon.toString());
            List<?> pLineList = parseLineArray.toJavaList((Class<? extends Object>) List.class);
            List<List<Double>> rLineList = new ArrayList<>();
            pLineList.forEach(line -> {
                JSONArray pLineArray = JSONArray.parseArray(line.toString());
                List<Double> pointList = pLineArray.toJavaList(Double.class);
                rLineList.add(pointList);
            });
            rPolygonList.add(rLineList);
        });
        return rPolygonList;
    }

    /**
     * 获取geoJson的coordinates(坐标数据)
     *
     * @param geoJson
     * @return
     */
    public static Iterable<?> getCoordinates(JSONObject geoJson) {
        String type = GeoJsonUtils.getType(geoJson);
        if (type != null) {
            if (GeoJsonTypeConstants.POINT.equals(type)) {
                return GeoJsonUtils.getPointCoordinates(geoJson);
            } else if (GeoJsonTypeConstants.LINESTRING.equals(type)) {
                return GeoJsonUtils.getLineStringCoordinates(geoJson);
            } else if (GeoJsonTypeConstants.POLYGON.equals(type)) {
                return GeoJsonUtils.getPolygonCoordinates(geoJson);
            } else {
                return null;
            }
        }
        return null;
    }

    /**
     * 获取geoJson的类型（type点线面）
     *
     * @param geoJson
     * @return
     */
    public static String getType(JSONObject geoJson) {
        if (null != geoJson) {
            return (String) geoJson.get(GeoJsonConstants.TYPE);
        } else {
            return null;
        }
    }

    /**
     * geoJson的coordinates转为数组（点）
     *
     * @param geoJson
     * @return
     */
    public static Object parsePointCoordinatesToObject(GeoJson geoJson) {
        try {
            //返回数据GeoJson时的解析
            //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
            GeoJsonPoint point = (GeoJsonPoint) geoJson;
            return point.getCoordinates().toArray();
        } catch (Exception e) {
            try {
                //输入geometry格式的jsonObject转为geoJson时的解析
                //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
                return geoJson.getCoordinates();
            } catch (Exception e1) {
                e1.printStackTrace();
                return null;
            }
        }
    }

    /**
     * geoJson的coordinates转为数组（线）
     *
     * @param geoJson
     * @return
     */
    public static Object parseLineStringCoordinatesToObject(GeoJson geoJson) {
        try {
            //返回数据GeoJson时的解析
            //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
            GeoJsonLineString lineString = (GeoJsonLineString) geoJson;
            List<Point> pointList = lineString.getCoordinates();
            List<Double[]> rLineList = new ArrayList<>();
            pointList.forEach(point -> {
                rLineList.add(new Double[]{point.getX(), point.getY()});
            });
            return rLineList.toArray();
        } catch (Exception e) {
            try {
                //输入geometry格式的jsonObject转为geoJson时的解析
                //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
                return geoJson.getCoordinates();
            } catch (Exception e1) {
                e1.printStackTrace();
                return null;
            }
        }
    }

    /**
     * geoJson的coordinates转为数组（面）
     *
     * @param geoJson
     * @return
     */
    public static Object parsePolygonCoordinatesToObject(GeoJson geoJson) {
        try {
            //返回数据GeoJson时的解析
            //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
            GeoJsonPolygon polygon = (GeoJsonPolygon) geoJson;
            List<GeoJsonLineString> lineList = polygon.getCoordinates();
            List<List<Double[]>> rPolygonList = new ArrayList<>();
            lineList.forEach(line -> {
                List<Point> pointList = line.getCoordinates();
                List<Double[]> rLineList = new ArrayList<>();
                pointList.forEach(point -> {
                    rLineList.add(new Double[]{point.getX(), point.getY()});
                });
                rPolygonList.add(rLineList);
            });
            return rPolygonList.toArray();
        } catch (Exception e) {
            try {
                //输入geometry格式的jsonObject转为geoJson时的解析
                //目的输出geometry格式的数据(coordinates的每个单元数据为Double)
                return geoJson.getCoordinates();
            } catch (Exception e1) {
                e1.printStackTrace();
                return null;
            }
        }
    }

    /**
     * geoJson转为jsonObject(geoJson地理位置展示)
     *
     * @param geoJson
     * @return
     */
    public static JSONObject parseGeoJsonToJSONObject(GeoJson geoJson) {
        if (geoJson != null && StringUtils.isNotBlank(geoJson.getType())) {
            JSONObject jsonObject = new JSONObject();
            String type = geoJson.getType();
            jsonObject.put(GeoJsonConstants.TYPE, type);
            if (GeoJsonTypeConstants.POINT.equals(type)) {
                jsonObject.put(GeoJsonConstants.COORDINATES, GeoJsonUtils.parsePointCoordinatesToObject(geoJson));
                return jsonObject;
            } else if (GeoJsonTypeConstants.LINESTRING.equals(type)) {
                jsonObject.put(GeoJsonConstants.COORDINATES, GeoJsonUtils.parseLineStringCoordinatesToObject(geoJson));
                return jsonObject;
            } else if (GeoJsonTypeConstants.POLYGON.equals(type)) {
                jsonObject.put(GeoJsonConstants.COORDINATES, GeoJsonUtils.parsePolygonCoordinatesToObject(geoJson));
                return jsonObject;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

}
